﻿#pragma once 
#define _USE_MATH_DEFINES
#include <iostream>
#include <limits>
#include <cmath>
#include<math.h>
#include <algorithm>
using namespace std;

double eps = numeric_limits<double>::epsilon();

class EquationSolver
{
public:
	virtual double solve() = 0;
};

class Function
{
public:
	virtual double operator()(double _x) = 0;
	virtual double diff(double _x) { return 0; }
};


class BisectionSolver : public EquationSolver
{
private:
	double a;
	double b;
	double del;
	int M;
	Function& f;
public:
	BisectionSolver(double _a, double _b, double _del, int _M, Function& _f) : a(_a), b(_b), del(_del), M(_M), f(_f) {};
	double solve()
	{
		double left = f(a);
		double right = f(b); 
		double len;
		double nowf;
		double now = 0;
		for (int i = 1; i <= M; i++)
		{
			len = b - a;
			now = a + len / 2;
			nowf = f(now);
			if (fabs(len) < del || fabs(nowf) < eps) break;
			else if (left > 0 && nowf < 0 || left < 0 && nowf > 0) {
				b = now;
				right = nowf;
			}
			else {
				a = now, left = nowf;
			}
		}
		return now;
	}
};

class NewtonSolver : public EquationSolver
{
private:
	double x0;
	double del;
	int M;
	Function& f;
public:
	NewtonSolver(double _x0, double _delta, int _M, Function& _f) : x0(_x0), del(_delta), M(_M), f(_f) {};
	double solve() {
		double x = x0, fx;
		for (int k = 0; k <= M; k++) {
			fx = f(x);
			if (fabs(fx) < eps)
				break;
			x = x - fx / f.diff(x);
		}
		return x;
	}
};

class SecantSolver : public EquationSolver
{
private:
	double x0, x1, del;
	int M;
	Function& f;
public:
	SecantSolver(double _x0, double _x1, double _delta, int _M, Function& _f) : x0(_x0), x1(_x1), del(_delta), M(_M), f(_f) {};
	double solve() {
		double xn = x1;
		double xn_1 = x0;
		double fx = f(xn);
		double v = f(xn_1);
		double s;
		for (int k = 2; k <= M; k++)
		{
			if (fabs(fx) > fabs(v)) {
				swap(xn, xn_1);
				swap(fx, v);
			}
			s = (xn - xn_1) / (fx - v);
			xn_1 = xn;
			v = fx;
			xn = xn - fx * s;
			fx = f(xn);
			if (fabs(xn - xn_1) < del || fabs(fx) < eps)
				break;
		}
		return xn;
	}
};